package com.cy.pj.web配置类和控制层.config配置类;

import com.cy.pj.Dao.SysUserDao;
import com.cy.pj.service.realm.ShiroRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
    /**
     * 定义Realm对象，通过此对象访问数据库中的用户和权限信息，并进行封装
     *@Bean注解描述方法时，表示方法的返回值要交给spring管理，这个bean的名字默认
     * 是方法名(realm())。还可以自己起名，例如@Bean("ShiroRealm")
     */
    @Bean
    public Realm realm(SysUserDao sysUserDao){
        //继承抽象类的类可以实例化:ShiroRealm ed=new ShiroRealm();
        return new ShiroRealm();
    }

    /**基于此对象定义过滤规则，例如哪些请求必须要认证，哪些请求需要认证
     *ShiroFilterChainDefinition 此对象中定义了若干过滤器Filter,基于
     *这些过滤器以及我们定义的过滤规则对业务进行实现.
     */
    @Bean  //认证=登录，登录成功就可以访问数据
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        //1，先定义过滤链对象--DefaultShiroFilterChainDefinition是ShiroFilterChainDefinition的实现类
        DefaultShiroFilterChainDefinition chainDefinition =
                new DefaultShiroFilterChainDefinition();

        //2,在过滤链里面添加自定义过滤器
//配置登陆操作不需要认证,(其中,anno表示匿名访问处理)
        chainDefinition.addPathDefinition("/user/login/**", "anon");
//配置登出操作,登出以后要跳转到登录页面(其中，logout表示系统登出处理)
        chainDefinition.addPathDefinition("/user/logout", "logout");

        //配置/user/login/**开头的资源，可以匿名访问(不用登录就可以访问),
        //其中 anon 为 shiro 框架指定的匿名过滤器
        //chainDefinition.addPathDefinition("/menu/**", "anon");

        //设置哪些资源需要认证才可以访问("/**"--对所有的资源访问都要认证)
        //这里表示对这个url(例如"/user/**"--"localhoset/user/"需要认证才能访问),
        //要调用过滤链中的哪个过滤器(例如authc)进行处理
        //chainDefinition.addPathDefinition("/user/**", "authc");
        //chainDefinition.addPathDefinition("/**", "authc");

        //配置 记住我--修改认证拦截器中的过滤配置，将 authc 替换为 user
        chainDefinition.addPathDefinition("/**", "user");
        //当我们访问一个需要认证以后才可以访问的资源时，但是你还没有认证，此时会跳转到登录页面
        return chainDefinition;
    }

    /**写这个类的原因:当把授权注解写在Controller的方法上时，底层会基于AOP创建Controller的代理对象，
     * 而代理对象会把方法上的请求格式ul(@GetMapping)去掉，所以就访问不了
     *
     * 当将shiro中的注解@RequiresPermissions放到Controller层方法上时需要配置此对象，
     * 并设置对控制层方法上的注解ul有效(例如@GetMapping)
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        //底层的设置默认是 false
        advisorAutoProxyCreator.setUsePrefix(true);
        return advisorAutoProxyCreator;
    }

    /**使用 Shiro 框架做认证和授权时,用户的登录信息会写到 Session 中,这个 session
    默认为 30 分钟,然后会将这个 session 的 id 以会话 cookie 的形式写到客户端.假如需要
    对 Session 相关进行配置，可参考如下配置方式。
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager=
                new DefaultWebSessionManager();
        //session 的超时时间--客户端过1分钟后，会让重新登录用户信息
        sessionManager.setGlobalSessionTimeout(1000*60*1);//1 分钟
        //sessionManager.setGlobalSessionTimeout(2*60*1000);//2 分钟
        //删除无效 session
        sessionManager.setDeleteInvalidSessions(true);
        //当客户端 cookie 被禁用是否要设置 url 重写
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**设置：记住我--第一次登陆成功，下次就可以不用写用户密码认证，直接就可以登录，查询了*/
    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager rememberMeManager=new CookieRememberMeManager();
        SimpleCookie cookie=new SimpleCookie("rememberMe");
        cookie.setMaxAge(7*24*60*60);//设置cookie的生命周期--7天
        rememberMeManager.setCookie(cookie);
        rememberMeManager.setCipherKey(
                Base64.decode("WGjHie9R114WBoCm3TAZ0g=="));
        return rememberMeManager;
    }

    //问题：每次客户端查询操作时，服务端realm都会访问数据库获取授权信息，比较消耗内存
   /** 解决:配置 Shiro 中自带的 CacheManager 对象,第一次查询操作时，此对象可以通过 Cache 缓存授权时获取到
    权限信息,下次再访问授权方法时不需要再访问数据库了,这样可以减少数据库的访问压力,
    同时提高授权性能.其配置方式如下:
    */
    /**配置授权缓存管理器，默认应用的缓存是shiro框架内置的缓存对象，
     *底层的缓存实现就是一个map*/
    @Bean
    protected CacheManager shiroCacheManager() {
        return new MemoryConstrainedCacheManager();
    }
}
